#include "Col.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "StreamBuf.h"
#include "String.h"
#include "Math.h"
#include "ET_stdio.h"

//---- StreamBuf ---------------------------------------------------------------

StreamBuf::StreamBuf(int sz, const char *p, int cnt, bool a)
{
	sb= 0;
	fpos= 0;
	setbuf(p, sz, cnt, a);
}

StreamBuf::~StreamBuf()
{
	//SafeDelete(sb);
	if (alloc)
		SafeDelete(base);
}

void StreamBuf::SetBaseStream(StreamBuf *s)
{
	sb= s;
}

StreamBuf *StreamBuf::GetBaseStream()
{
	return sb;
}

StreamBuf *StreamBuf::setbuf(const char *p, int len, int off, bool all)
{
	/*
	if (p && base && p < base && p > eptr) {
		if (alloc)
			delete base;
	}
	*/
	if (p == 0 && len > 0) {
		p= new char[len];
		all= TRUE;
	}
	base= ptr= (char*) p;
	eptr= base + len;
	gptr= base + off;
	fpos= 0;
	alloc= all;
	return this;
}

void StreamBuf::Finish()
{
	if (gptr == base) {    // write
		overflow(EOF);
		Close();
	}
	//if (sb)
	//    sb->Finish();
}

int StreamBuf::overflow(int c)
{
	int cnt;

	if (base == eptr) {             // unbuffered IO
		if (c != EOF) {
			char buf[1];
			buf[0]= c;
			cnt= Overflow((u_char*)buf, 1);
			fpos+= cnt;
			if (cnt != 1)
				return EOF;
		}
	} else {                        // buffered IO
		if (ptr > base) {
			int wcnt= ptr-base;
			cnt= Overflow((u_char*)base, wcnt);
			fpos+= cnt;
			if (cnt != wcnt)
				return EOF;
			ptr= gptr= base;
			if (c != EOF)
				*ptr++= c;
		}
	}
	return zapeof(c);
}

int StreamBuf::underflow()
{
	fpos+= (long)(gptr-base);
	int cnt= Underflow((u_char*)base, eptr-base);

	if (cnt < 1)
		return EOF;

	ptr= base;
	gptr= base+cnt;
	return zapeof(*ptr);
}

int StreamBuf::sputn(const char *s, int n)
{
	register int avail= eptr-ptr;
	register int req= n;

	while (avail < req) {
		MemCpy(ptr, s, avail);
		s+= avail;
		ptr+= avail;
		req-= avail;
		if (overflow(zapeof(*s++)) == EOF)
			return n-req ;
		--req ;
		avail= eptr-ptr;
	}
	MemCpy(ptr, s, req);
	ptr+= req;
	return n;
}

int StreamBuf::sgetn(char *s, int n)
{
	register int avail= gptr-ptr;
	register char* p= s;
	register int req= n;

	while (req > avail) {
		MemCpy(p, ptr, avail) ;
		p+= avail;
		req-= avail;
		ptr+= avail;
		if (underflow() == EOF)
			return p-s;
		avail= gptr-ptr;
	}

	if (req > 0) {
		MemCpy(p, ptr, req);
		ptr+= req;
	}
	return n;
}

void StreamBuf::sputbackc(char c)
{
	if (ptr > base) {
		ptr--;
		if (*ptr != c)
			fprintf(stderr, "putback(%c) was: %c\n", c, *ptr);
	} else
		fprintf(stderr, "putback error\n");
}

int StreamBuf::seek(long pos, bool relative)
{
	long oldpos= tell();
	long newpos= pos;
	if (relative)
		newpos+= oldpos;
		
	if (newpos == oldpos)
		return oldpos;

	// fprintf(stderr, "seek(%d,%d): %d <= %d < %d\n", pos, relative, fpos, newpos, fpos+(gptr-base));

	if (newpos > oldpos) {
		//if (newpos > (eptr-base))     // wrong fix from joe
		//    newpos= eptr-base;
		long diff= newpos-oldpos;
		for (long i= 0; i < diff; i++)
			stossc();
		return newpos;
	}

	if (newpos >= fpos && newpos < fpos+(gptr-base))
		ptr+= newpos-oldpos;
	else {
		/*
		fpos= newpos/size();
		Seek(fpos, 0);
		ptr= gptr= base+(newpos % size());
		*/
		fpos= newpos;
		Seek(fpos, 0);
		ptr= gptr= base;
	}
	return newpos;
}

int StreamBuf::Underflow(u_char*, int)
{
	return EOF;
}

int StreamBuf::Overflow(u_char*, int)
{
	return EOF;
}

void StreamBuf::Close()
{
}

long StreamBuf::Seek(long, int)
{
	return EOF;
}

int StreamBuf::salloc(char **buf, int n)
{
	register int avail= gptr-ptr;

	if (avail <= 0) {
		if (underflow() == EOF)
			return EOF;
		avail= gptr-ptr;
	}
	*buf= ptr;
	if (avail < n)
		n= avail;
	ptr+= n;
	return n;
}

